package edu.pku.hawk.controller.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import edu.pku.hawk.controller.util.Constants.FusionAlgorithm;
import edu.pku.hawk.controller.util.Constants.FusionMode;
import edu.pku.hawk.controller.util.Constants.ServiceAccessMode;

public class Config {

	/*
	 * Config
	 */
	public static long MonitorLoopInterval = 400;
	public static int MonitorBuffer = 10;
	public static long QueryRange = 1000;
	public static final boolean UsingTimerOrSleep = false;
	public static ServiceAccessMode DBSetting = ServiceAccessMode.LocalDBService;
	public static FusionMode FusionSetting = FusionMode.LocalSer;
	public static FusionAlgorithm FusionImpl = FusionAlgorithm.Average;
	public static boolean ResourceRequest = true;

	public static int PressureThreshold = 30;

	private HashMap<String, DetectorInfo> DetectorInfoMap = new HashMap<String, DetectorInfo>();
	private HashMap<Integer, List<String>> SensorIdByType = new HashMap<Integer, List<String>>();
	private HashMap<Integer, SerResource> SerRscMapByType = new HashMap<Integer, SerResource>();

	public static class Singleton {
		public static Config Instance = new Config();
	}

	private Config() {
		// Must load property first and load battlefield then.

		try {
			loadProperty();
		} catch (Exception ex) {
			// ex.printStackTrace();
			System.out.println("No config file.");
		}

		try {
			loadBattlefield();
			List<DetectorInfo> l = getSersorInfoList();
			for (DetectorInfo di : l) {
				List<String> list = SensorIdByType.get(di.type);
				if (list == null) {
					list = new ArrayList<String>();
				}
				list.add(di.id);
				SensorIdByType.put(di.type, list);
			}

			loadSerRsc();
			
			loadDevice();

		} catch (Exception ex) {
			// ex.printStackTrace();
			System.out.println("No config file.");
		}
	}

	private void loadProperty() throws Exception {
		Properties ppt = Util.loadPropertyFile(Constants.Properties_File_Path);

		if (!ppt.containsKey("MQServer_Name"))
			throw new Exception("Properties file error! (MQServer_Name)");
		if (!ppt.containsKey("MQServer_Password"))
			throw new Exception("Properties file error! (MQServer_Password)");
		if (!ppt.containsKey("MQServer_URL"))
			throw new Exception("Properties file error! (MQServer_URL)");
		if (!ppt.containsKey("Battlefield_File_Path"))
			throw new Exception("Properties file error! (Battlefield_File_Path)");
		if (!ppt.containsKey("Service_Resource_File_Path"))
			throw new Exception("Properties file error! (Battlefield_File_Path)");
		if (!ppt.containsKey("DataSource_URL"))
			throw new Exception("Properties file error! (DataSource_URL)");
		if (!ppt.containsKey("DataSource_Name"))
			throw new Exception("Properties file error! (DataSource_Name)");
		if (!ppt.containsKey("DataSource_Password"))
			throw new Exception("Properties file error! (DataSource_Password)");
		if (!ppt.containsKey("DataSource_DB"))
			throw new Exception("Properties file error! (DataSource_DB)");

		DBSetting = ServiceAccessMode.valueOf(Util.trim(ppt.getProperty("DBSetting")));
		FusionSetting = FusionMode.valueOf(Util.trim(ppt.getProperty("FusionSetting")));
		FusionImpl = FusionAlgorithm.valueOf(Util.trim(ppt.getProperty("FusionImpl")));
		ResourceRequest = Util.trim(ppt.getProperty("ResourceRequest")).equalsIgnoreCase("false") ? false : true;

		PressureThreshold = Integer.valueOf(Util.trim(ppt.getProperty("PressureThreshold")));
		MonitorLoopInterval = Long.valueOf(Util.trim(ppt.getProperty("MonitorLoopInterval")));
		MonitorBuffer = Integer.valueOf(Util.trim(ppt.getProperty("MonitorBuffer")));

		Constants.MQServer_Name = Util.trim(ppt.getProperty("MQServer_Name"));
		Constants.MQServer_Password = Util.trim(ppt.getProperty("MQServer_Password"));
		Constants.MQServer_URL = Util.trim(ppt.getProperty("MQServer_URL"));
		Constants.Battlefield_File_Path = Util.trim(ppt.getProperty("Battlefield_File_Path"));
		Constants.Service_Resource_File_Path = Util.trim(ppt.getProperty("Service_Resource_File_Path"));
		Constants.Battlefield_Radio = Double.valueOf(Util.trim(ppt.getProperty("Battlefield_Radio")));
		Constants.PrintLog = Boolean.valueOf(Util.trim(ppt.getProperty("PrintLog")));
		Constants.DataSource_URL = Util.trim(ppt.getProperty("DataSource_URL"));
		Constants.DataSource_Name = Util.trim(ppt.getProperty("DataSource_Name"));
		Constants.DataSource_Password = Util.trim(ppt.getProperty("DataSource_Password"));
		Constants.DataSource_DB = Util.trim(ppt.getProperty("DataSource_DB"));
		Constants.DataSource_Using = Boolean.valueOf(Util.trim(ppt.getProperty("DataSource_Using")));
		Constants.RFID_URL = Util.trim(ppt.getProperty("RFID_URL"));
		Constants.RFID_Name = Util.trim(ppt.getProperty("RFID_Name"));
		Constants.RFID_Password = Util.trim(ppt.getProperty("RFID_Password"));
		Constants.RFID_DB = Util.trim(ppt.getProperty("RFID_DB"));
		Constants.RFID_Using = Boolean.valueOf(Util.trim(ppt.getProperty("RFID_Using")));
		Constants.UDDI_Server_URL=Util.trim(ppt.getProperty("UDDI_Server_URL"));
	}

	/**
	 * The method is a fake method used to initialize config. Actual loading
	 * operation is in the construction method.
	 */
	public void initialize() {
	}

	private void loadBattlefield() throws Exception {
		ArrayList<String> blines = Util.readFileLines(Constants.Battlefield_File_Path);

		if (blines == null || blines.size() < 1)
			throw new Exception("battlefield.txt error");
		String firstLinet = blines.get(0);
		String[] whs = firstLinet.split(",");
		if (whs.length != 2)
			throw new Exception("battlefield.txt error");

		Constants.Battlefield_Width = (int) (Integer.valueOf(whs[0]) * Constants.Battlefield_Radio);
		Constants.Battlefield_Height = (int) (Integer.valueOf(whs[1]) * Constants.Battlefield_Radio);

		for (int i = 1; i < blines.size(); i++) {
			String line = blines.get(i);
			if (Util.isBlankOrNull(line.trim())) {
				continue;
			}
			StringTokenizer token = new StringTokenizer(line, " ");
			String id = token.nextToken();
			if (Integer.parseInt(id) < 0)
				throw new Exception("battlefield.txt error");
			String xy = token.nextToken();
			String[] xys = xy.split(",");
			if (xys == null || (xys.length != 2 && xys.length != 4))
				throw new Exception("battlefield.txt error");
			int x = 0, y = 0, x1 = 0, y1 = 0;
			if (xys.length == 2) {
				x = (int) (Integer.valueOf(xys[0]) * Constants.Battlefield_Radio);
				y = (int) (Integer.valueOf(xys[1]) * Constants.Battlefield_Radio);
			} else if (xys.length == 4) {
				x = (int) (Integer.valueOf(xys[0]) * Constants.Battlefield_Radio);
				y = (int) (Integer.valueOf(xys[1]) * Constants.Battlefield_Radio);
				x1 = (int) (Integer.valueOf(xys[2]) * Constants.Battlefield_Radio);
				y1 = (int) (Integer.valueOf(xys[3]) * Constants.Battlefield_Radio);
			}
			String shortName = token.nextToken();
			String tableName = getDetectorTableName(shortName);
			int type = getDetectorType(shortName);
			DetectorInfo di = new DetectorInfo(id, shortName, tableName, type, x, y, x1, y1);
			DetectorInfoMap.put(id, di);
		}
	}

	private void loadSerRsc() throws Exception {
		ArrayList<String> blines = Util.readFileLines(Constants.Service_Resource_File_Path);
		Util.logln(1);

		if (blines == null || blines.size() < 1)
			throw new Exception("serviceprofile.txt error");

		for (int i = 1; i < blines.size(); i++) {
			String line = blines.get(i);
			if (Util.isBlankOrNull(line.trim())) {
				continue;
			}
			if (!line.trim().startsWith("#")) {
				StringTokenizer token = new StringTokenizer(line, " ");
				SerResource sr = new SerResource();
				sr.setServiceID(Integer.parseInt(token.nextToken()));
				sr.setCpu(Double.parseDouble(token.nextToken()));
				sr.setMem(Double.parseDouble(token.nextToken()));
				sr.setNet(Double.parseDouble(token.nextToken()));
				sr.setDisk(Double.parseDouble(token.nextToken()));
				sr.setServiceIcon(new String(token.nextToken()));
				String stype = token.nextToken();
				sr.setServiceName(token.nextToken());
				if (stype.equalsIgnoreCase(SerResource.BASISTYPE) && stype.equalsIgnoreCase(SerResource.COMPOSEDTYPE)) {
					throw new Exception("service type error in servicedprofile.txt.");
				}
				sr.setServiceType(stype);
				SerRscMapByType.put(sr.getServiceID(), sr);
			}
		}
	}

	private void loadDevice() throws Exception {
		ArrayList<String> blines = Util.readFileLines(Constants.Device_File_Path);

		if (blines == null || blines.size() < 1)
			throw new Exception("device.txt error");

		for (int i = 0; i < blines.size(); i++) {
			String line = blines.get(i);
			if (Util.isBlankOrNull(line.trim())) {
				continue;
			}
			StringTokenizer token = new StringTokenizer(line, " \t");
			Integer id = Integer.valueOf(token.nextToken());
			if (!token.hasMoreTokens())
				continue;
			Integer deviceAddr = Integer.valueOf(token.nextToken());
			if (!token.hasMoreTokens())
				continue;
			String switchStr = token.nextToken();
			String ss = new String(switchStr.substring(switchStr.length()-1));
			Integer switchNum = Integer.valueOf(ss);
			if (!token.hasMoreTokens())
				continue;
			String property = token.nextToken();

			DetectorInfo dinfo = DetectorInfoMap.get(((Integer)id).toString());
			if (dinfo == null)
				throw new Exception("device.txt error. no such sensor id.");
			dinfo.deviceAddr = deviceAddr;
			dinfo.switchNum = switchNum;
			dinfo.property = property;
			if (dinfo.type == Constants.TYPE_PRESSURE_SENSOR) {
				dinfo.threshold = Integer.valueOf(property);
			}
			
		}
	}

	public ArrayList<DetectorInfo> getSersorInfoList() {
		ArrayList<DetectorInfo> list = new ArrayList<DetectorInfo>();
		Iterator<Map.Entry<String, DetectorInfo>> it = DetectorInfoMap.entrySet().iterator();
		while (it.hasNext()) {
			list.add(it.next().getValue());
		}
		return list;
	}

	public DetectorInfo getDetectorInfo(String detectorId) {
		return DetectorInfoMap.get(detectorId);
	}

	public int getDetectorType(String shortName) {
		if (shortName.equalsIgnoreCase(Constants.Infrared_Short_name))
			return Constants.TYPE_INFRARED_SENSOR;
		if (shortName.equalsIgnoreCase(Constants.Sound_Short_name))
			return Constants.TYPE_SOUND_SENSOR;
		if (shortName.equalsIgnoreCase(Constants.Fog_Short_name))
			return Constants.TYPE_FOG_SENSOR;
		if (shortName.equalsIgnoreCase(Constants.Metal_Short_name))
			return Constants.TYPE_METAL_SENSOR;
		if (shortName.equalsIgnoreCase(Constants.Vedio_Short_name))
			return Constants.TYPE_VEDIO_SENSOR;
		if (shortName.equalsIgnoreCase(Constants.Pressure_Short_name))
			return Constants.TYPE_PRESSURE_SENSOR;
		if (shortName.equalsIgnoreCase(Constants.RFID_Short_name))
			return Constants.TYPE_RFID_SENSOR;
		else
			return Constants.TYPE_UNKNOWN;
	}

	public String getDetectorTableName(String shortName) {
		if (shortName.equalsIgnoreCase(Constants.Infrared_Short_name))
			return Constants.Infrared_TableName;
		if (shortName.equalsIgnoreCase(Constants.Sound_Short_name))
			return Constants.Sound_TableName;
		if (shortName.equalsIgnoreCase(Constants.Fog_Short_name))
			return Constants.Fog_TableName;
		if (shortName.equalsIgnoreCase(Constants.Metal_Short_name))
			return Constants.Metal_TableName;
		if (shortName.equalsIgnoreCase(Constants.Pressure_Short_name))
			return Constants.Pressure_TableName;
		if (shortName.equalsIgnoreCase(Constants.RFID_Short_name))
			return Constants.RFID_TableName;
		else
			return null;
	}

	public List<String> getSensorIdListByType(int type) {
		List<String> ids = SensorIdByType.get(type);
		if (ids == null) {
			ids = new ArrayList<String>();
		}
		return ids;

	}

	public static void main(String[] args) {
		Config f = new Config();
		int i = 0;
		i++;
		Config gg = f;
		long g = f.MonitorLoopInterval;
		g++;

	}

	public HashMap<Integer, SerResource> getSerRscMapByType() {
		return SerRscMapByType;
	}
}
